/*
 *  Copyright © OpenAtom Foundation.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.openatom.ubml.model.common.definition.commonmodel.entity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.openatom.ubml.common.util.StringUtils;
import org.openatom.ubml.model.common.definition.cef.element.GspAssociation;
import org.openatom.ubml.model.common.definition.cef.entity.DataTypeAssemblyInfo;
import org.openatom.ubml.model.common.definition.cef.variable.CommonVariableEntity;
import org.openatom.ubml.model.common.definition.commonmodel.IGspCommonElement;
import org.openatom.ubml.model.common.definition.commonmodel.IGspCommonModel;
import org.openatom.ubml.model.common.definition.commonmodel.IGspCommonObject;
import org.openatom.ubml.model.common.definition.commonmodel.entity.element.GspCommonAssociation;
import org.openatom.ubml.model.common.definition.entity.AbstractMetadataContent;

/**
 * The Definition Of Common Model
 *
 * @ClassName: GspCommonModel
 * @Author: Benjamin Gong
 * @Date: 2021/1/11 17:13
 * @Version: V1.0
 */
public class GspCommonModel extends AbstractMetadataContent implements IGspCommonModel, Cloneable {
    private static final String coreNamespaceSuffix = "core";
    private static final String entityNamespaceSuffix = "entity";
    private static final String apiNamespaceSuffix = "api";
    private final java.util.HashMap<String, String> extendNodeList = new java.util.HashMap<String, String>();
    // region 字段和属性
//	private String recordHistoryTable = "GspDATALOG";
//	private boolean isRecord;
    private boolean isVirtual;
    private GspCommonObject mainObject;
    // private java.util.ArrayList<RelationForeignKeyConstraint> fkConstraints = new java.util.ArrayList<RelationForeignKeyConstraint>();
    private CommonVariableEntity variables;
    // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET
    // attributes:
    // [NonSerialized]
    private java.util.Map<String, Object> extProperties;
//	private boolean isAllowDerive;
//	private boolean isAllowExtend;
//	private String dimension = "";
//	private String hierarchy = "";
    private String extendType = "";
    private String privateID;
    private String privateCode;
    private String privateName;
    /**
     * 标签
     */
    private List<String> beLabel;
    /**
     * 模型实体类型，表示不同模型实例的类型
     */
    private String privateEntityType;
    /**
     * 外键约束
     */
    // public java.util.ArrayList<RelationForeignKeyConstraint> getFkConstraints() {
    // 	return fkConstraints;
    // }

    private String generatingAssembly = "";
    private String generatingDotnetAssembly = "";
    /**
     * 是否使用命名空间+编号的ConfigId 以前建的为false，保证原有代码可用 新建元数据为true，保证后续元数据ConfigId唯一
     */
    private boolean privateIsUseNamespaceConfig = false;
    private VersionControlInfo privateVersionContronInfo;
    /**
     * 国际化项前缀
     */
    private String privateI18nResourceInfoPrefix;

    public GspCommonModel() {
        setVariables(new CommonVariableEntity());
        setIsVirtual(false);
    }

    /**
     * 扩展成员,用于扩展方法暂存运行时临时数据
     */
    // [Newtonsoft.Json.jsonIgnore()]
    public java.util.Map<String, Object> getExtProperties() {
        return (extProperties != null) ? extProperties : (extProperties = new HashMap<String, Object>());
    }

    public final String getID() {
//		this.privateID = super.getId();
        return privateID;
    }

    public final void setID(String value) {
        privateID = value;
//		super.setId(value);
    }

    public final String getCode() {
        this.privateCode = super.getCode();
        return privateCode;
    }

    public final void setCode(String value) {
        privateCode = value;
        super.setCode(value);
    }

    public final String getName() {
        this.privateName = super.getName();
        return privateName;
    }

    public final void setName(String value) {
        privateName = value;
        super.setName(value);
    }

    public List<String> getBeLabel() {
        if (beLabel == null) {
            beLabel = new ArrayList<String>();
            return beLabel;
        }
        return beLabel;
    }

    public void setBeLabel(List<String> value) {
        beLabel = value;
    }

    /**
     * 是否虚拟
     */
    public boolean getIsVirtual() {
        return isVirtual;
    }

    public void setIsVirtual(boolean value) {
        isVirtual = value;
    }

    /**
     * 扩展类型
     */
    public String getExtendType() {
        return extendType;
    }

    public void setExtendType(String value) {
        extendType = value;
    }

    public final String getEntityType() {
        return privateEntityType;
    }

    public final void setEntityType(String value) {
        privateEntityType = value;
    }

    /**
     * 扩展结点列表
     */
    // [Newtonsoft.Json.jsonIgnore()]
    public java.util.HashMap<String, String> getExtendNodeList() {
        return extendNodeList;
    }

    /**
     * 主实体
     */
    @Override
    public GspCommonObject getMainObject() {
        return mainObject;
    }

    @Override
    public void setMainObject(IGspCommonObject value) {
        setCmMainObject((GspCommonObject)value);
    }

    public void setCmMainObject(GspCommonObject value) {
        if (mainObject != null) {
            mainObject.setBelongModel(null);
        }
        mainObject = value;
        if (mainObject != null) {
            mainObject.setBelongModel(this);
        }
    }

    /**
     * 主节点主键字段Id
     */
    public final String getPrimayKeyID() {
        String result;
        if (getMainObject() == null || getMainObject().getColumnGenerateID() == null) {
            result = "";
        } else {
            String text = getMainObject().getColumnGenerateID().getElementID();
            IGspCommonElement element = findElementById(text);
            if (element != null) {
                text = element.getLabelID();
            }
            result = (text != null) ? text : "";
        }
        return result;
    }

    public String getGeneratingAssembly() {
        return generatingAssembly;
    }

    public void setGeneratingAssembly(String value) {
        generatingAssembly = value;
    }

    public String getDotnetGeneratingAssembly() {
        return generatingDotnetAssembly;
    }

    public void setDotnetGeneratingAssembly(String value) {
        generatingDotnetAssembly = value;
    }

    /**
     * 变量实体
     */
    public CommonVariableEntity getVariables() {
        return variables;
    }

    public void setVariables(CommonVariableEntity value) {
        variables = value;
    }

    public final boolean getIsUseNamespaceConfig() {
        return privateIsUseNamespaceConfig;
    }

    public final void setIsUseNamespaceConfig(boolean value) {
        privateIsUseNamespaceConfig = value;
    }

    public final VersionControlInfo getVersionContronInfo() {
        if (privateVersionContronInfo == null) {
            privateVersionContronInfo = new VersionControlInfo();
        }
        return privateVersionContronInfo;
    }

    // endregion

    // region 方法

    // region 获取字段

    public final void setVersionContronInfo(VersionControlInfo value) {
        privateVersionContronInfo = value;
    }

    public final String getI18nResourceInfoPrefix() {
        return privateI18nResourceInfoPrefix;
    }

    public final void setI18nResourceInfoPrefix(String value) {
        privateI18nResourceInfoPrefix = value;
    }

    /**
     * 根据Id查找特定字段
     *
     * @param elementId
     * @return
     */
    public final IGspCommonElement findElementById(String elementId) {
        if (getMainObject().getContainElements() != null && getMainObject().getContainElements().size() > 0) {
            for (int i = 0; i < getMainObject().getContainElements().size(); i++) {
                IGspCommonElement element = getMainObject().getContainElements().getItem(i);
                element = findElementFromElementById(element, elementId);
                if (element != null) {
                    return element;
                }
            }
            for (int i = 0; i < getMainObject().getContainChildObjects().size(); i++) {
                IGspCommonObject childObject = getMainObject().getContainChildObjects().get(i);
                IGspCommonElement element;
                for (int j = 0; j < childObject.getContainElements().size(); j++) {
                    element = childObject.getContainElements().getItem(j);
                    element = findElementFromElementById(element, elementId);
                    if (element != null) {
                        return element;
                    }
                }
                element = findEleInChildObject(childObject, elementId);
                if (element != null) {
                    return element;
                }
            }
        }

        return null;
    }

    private IGspCommonElement findElementFromElementById(IGspCommonElement ele, String elementId) {
        IGspCommonElement result;
        if (elementId.equals(ele.getID())) {
            result = ele;
        } else {
            if (ele.getHasAssociation()) {
                for (int i = 0; i < ele.getChildAssociations().size(); i++) {
                    GspAssociation association = ele.getChildAssociations().get(i);
                    for (int j = 0; j < association.getRefElementCollection().size(); j++) {
                        IGspCommonElement element = findElementFromElementById(
                                (IGspCommonElement)association.getRefElementCollection().get(j), elementId);
                        if (element != null) {
                            result = element;
                            return result;
                        }
                    }
                }
            }
            result = null;
        }
        return result;
    }

    // /**
    // * 重新获取字段缓存
    // *
    // * VM联动后，可能增加一些字段，需要重新获取字段缓存列表
    // */
    // public final void reGetObjectElements() {
    // final String elementDicKey = "NodeElementDic";
    // for (IGspCommonObject objItem : GetAllObjectList()) {
    // GspCommonObject objectItem = (GspCommonObject) objItem;
    // if (objectItem == null) {
    // throw new RuntimeException("错误的Object类型");
    // }

    // if (objectItem.ExtProperties.containsKey(elementDicKey)) {
    // objectItem.ExtProperties.remove(elementDicKey);
    // }
    // objectItem.getElements();
    // }
    // }
    // endregion

    // region 获取节点

    private IGspCommonElement findEleInChildObject(IGspCommonObject absObject, String elementId) {
        if (absObject.getContainChildObjects() != null && absObject.getContainChildObjects().size() > 0) {
            for (int k = 0; k < absObject.getContainChildObjects().size(); k++) // 子对象的循环
            {
                IGspCommonObject child = absObject.getContainChildObjects().get(k);
                for (int i = 0; i < child.getContainElements().size(); i++) // 包含元素循环
                {
                    IGspCommonElement ele = child.getContainElements().getItem(i);
                    ele = findElementFromElementById(ele, elementId);
                    if (ele != null) {
                        return ele;
                    }
                }
                findEleInChildObject(child, elementId); // 递归实现对多层子对象的查找
            }
        }
        return null;
    }

    /**
     * 获取所有节点的所有字段
     *
     * @param containRef 是否包含关联带出字段
     * @return
     */
    public final java.util.ArrayList<IGspCommonElement> getAllElementList(boolean containRef) {
        java.util.ArrayList<IGspCommonElement> elements = new java.util.ArrayList<IGspCommonElement>();
        java.util.ArrayList<IGspCommonObject> objectList = getAllObjectList();
        for (IGspCommonObject commonObject : objectList) {
            elements.addAll(commonObject.getAllElementList(containRef));
        }
        return elements;
    }

    /**
     * 获取所有字段
     *
     * @return
     */
    public final java.util.ArrayList<IGspCommonElement> getAllObjectElementList() {
        java.util.ArrayList<IGspCommonElement> elementList = new java.util.ArrayList<IGspCommonElement>();
        java.util.ArrayList<IGspCommonObject> objects = getAllObjectList();
        for (IGspCommonObject commonObject : objects) {
            elementList.addAll(commonObject.getAllElementList(true));
        }
        return elementList;
    }
    // endregion

    /**
     * 使用先根方式获取素有结点
     *
     * @return
     */
    public final java.util.ArrayList<IGspCommonObject> getAllObjectList() {
        // 通过堆栈递归取子孙节点，有空测一下复杂的场景对不对 ZhangJ2015年12月17日
        java.util.Stack<IGspCommonObject> stack = new java.util.Stack<IGspCommonObject>();
        java.util.ArrayList<IGspCommonObject> objectList = new java.util.ArrayList<IGspCommonObject>();
        if (mainObject == null) {
            return objectList;
        }
        stack.push(mainObject);
        while (stack.size() > 0) {
            IGspCommonObject obj = stack.pop();
            objectList.add(obj);
            for (IGspCommonObject childObj : obj.getContainChildObjects()) {
                stack.push(childObj);
            }
        }
        return objectList;
        // if (this.mainObject != null)
        // {
        // objectList.add(this.mainObject);
        // objectList.addRange(this.mainObject.getContainChildObjects());
        // }
        // return objectList;
    }

    /**
     * 根据代码查找节点
     *
     * @param code
     * @return
     */
    public final IGspCommonObject findObjectByCode(String code) {
        for (IGspCommonObject obj : getAllObjectList()) {
            if (code.equals(obj.getCode())) {
                return obj;
            }
        }
        return null;
    }

    /**
     * 根据Id查找节点
     *
     * @param objectId 节点Id
     * @return
     */
    public final IGspCommonObject findObjectById(String objectId) {
        for (IGspCommonObject obj : getAllObjectList()) {
            if (objectId.equals(obj.getID())) {
                return obj;
            }
        }
        return null;
    }

    // region Schema

    // public final String getModelSchema() {
    // CommonModelSchemaBuilder builder = new CommonModelSchemaBuilder(this);
    // return builder.buildSchema();
    // }

    /**
     * 获取主键Id集合
     *
     * @return
     */
    public final java.util.ArrayList<String> getPKConstraintIDList() {
        java.util.ArrayList<String> pkConstraintIdList = new java.util.ArrayList<String>();
        java.util.ArrayList<IGspCommonElement> elementList = getAllElementList(true);

        for (IGspCommonElement element : elementList) {
            if (element.getHasAssociation()) {
                for (GspAssociation item : element.getChildAssociations()) {
                    GspCommonAssociation association = (GspCommonAssociation)item;
                    pkConstraintIdList.add(association.getId());
                }

            }
        }
        return pkConstraintIdList;
    }

    /**
     * 克隆
     *
     * @return
     */
    public GspCommonModel clone() {
        GspCommonModel tempVar;
        try {
            tempVar = (GspCommonModel)super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
        GspCommonModel absModel = (GspCommonModel)((tempVar instanceof GspCommonModel) ? tempVar : null);
        if (absModel != null) {
            IGspCommonObject tempVar2 = mainObject.clone(mainObject.getParentObject());
            absModel.mainObject = (GspCommonObject)((tempVar2 instanceof GspCommonObject) ? tempVar2 : null);
        }
        return absModel;
    }

    /**
     * 字段编号、标签转camel
     *
     * @return
     */
    public GspCommonModel convertToCamelCaseProperty() {
        GspCommonModel be = clone();
        be.getAllElementList(true).stream().forEach(item -> {
            item.setCode(StringUtils.toCamelCase(item.getCode()));
            item.setLabelID(StringUtils.toCamelCase(item.getLabelID()));
        });
        return be;
    }

    //	public final CMAssemblyInfo getEntityAssemblyInfo() {
//		String assName = String.format("%1$s.%2$s.%3$s", getGeneratingAssembly(), this.getCode(),
//				entityNamespaceSuffix);
//		return new CMAssemblyInfo(assName, assName);
//	}
//
    private String getLowerCaseModelCode() {
        return getCode().toLowerCase();
    }

    @Override
    public DataTypeAssemblyInfo getCoreAssemblyInfo() {
        String assName = String.format("%1$s.%2$s.%3$s", getGeneratingAssembly(), this.getLowerCaseModelCode(),
                coreNamespaceSuffix);
        return new DataTypeAssemblyInfo(assName, assName);
    }

    // public DataTypeAssemblyInfo getEntityAssemblyInfo()
    // {
    // string assName = string.format("{0}.{1}.{2}", GeneratingAssembly, this.getCode(),
    // entityNamespaceSuffix);
    // return new DataTypeAssemblyInfo(assName, assName);
    // }
//	public final CMAssemblyInfo getApiNamespace() {
//		String assName = String.format("%1$s.%2$s.%3$s", getGeneratingAssembly(), this.getCode(), apiNamespaceSuffix);
//		return new CMAssemblyInfo(assName, assName);
//	}

    @Override
    public DataTypeAssemblyInfo getEntityAssemblyInfo() {
        String assName = String.format("%1$s.%2$s.%3$s", getGeneratingAssembly(), this.getLowerCaseModelCode(),
                entityNamespaceSuffix);
        return new DataTypeAssemblyInfo(assName, assName);
    }

    @Override
    public DataTypeAssemblyInfo getApiNamespace() {
        String assName = String.format("%1$s.%2$s.%3$s", getGeneratingAssembly(), this.getLowerCaseModelCode(), apiNamespaceSuffix);
        return new DataTypeAssemblyInfo(assName, assName);
    }

    public final String getGeneratedConfigID() {
        if (getIsUseNamespaceConfig()) {
            return getGeneratingAssembly() + "." + getCode();
        }
        return getCode();
    }

    public final String getDotnetGeneratedConfigID() {
        if (getIsUseNamespaceConfig()) {
            return getDotnetGeneratingAssembly() + "." + getCode();
        }
        return getCode();
    }
}
